/* 
 * This file is part of FreeBMS - an open source battery management system
 *
 * Serial Functions
 * 
 * NOTE: This code is designed to be compiled using SOURCEBOOST (http://www.sourceboost.com)
 */
 
/* This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
/*************************************************************
* Blocking serial interface code for the hardware UART
* of PIC16-range microcontrollers from Microchip
*
* (c) Lieven Hollevoet
* most recent version on http://boostc.lika.be
* Based on code from Yann Hendrikx.
*************************************************************/

// Communication debugging
//#define DEBUG 1

#include "serial.h"
#include "codes.h"

////////////////////////////////////////////////////////////
// Hardware initialisation
// input argument: the baudrate scaler
////////////////////////////////////////////////////////////
void serial_init(char brg)
{
	// Transmit control register
	txsta.TX9     = 0;
	txsta.TXEN    = 1;
	txsta.SYNC    = 0;
	txsta.BRGH    = 1;
	
	// Receive control register
	rcsta.SPEN    = 1;
	rcsta.RX9     = 0;
	rcsta.CREN    = 1;
	rcsta.ADDEN   = 0;
	
	// Baudcon register, if supported by the target architecture
#ifdef baudcon
	baudcon.BRG16 = 0;
	baudcon.WUE   = 0;
	baudcon.ABDEN = 0;
#endif
	// Baud rate setting
    spbrg 	   = brg;	
 }

////////////////////////////////////////////////////////////
// Print a single character to the serial port
// This funtion is skipped in debug mode since it will 
// block the debugger (the function waits for a hardware
// signal)
////////////////////////////////////////////////////////////
void serial_printf(char value)
{

#ifdef DBG_COMPILE
	// Skip when debugging
#warning "Debug build, serial_printf will return immediately"
	return;
#endif

    while((txsta & 1 << TRMT) == 0); 
    txreg = value;
}
 
////////////////////////////////////////////////////////////
// Print a hex value to the serial port
////////////////////////////////////////////////////////////
void serial_print_hex(char number)
{
    char hexChar;
    char i;
    for(i = 0; i < 2; i++)
    {
        if(i == 0)
            hexChar = number >> 4;
        else
            hexChar = number & 0x0F;
        if(hexChar < 10)
            hexChar = hexChar + '0';
        else
            hexChar = hexChar + ('A' - 10);
        serial_printf(hexChar);
    }
}

////////////////////////////////////////////////////////////
// Wait for a character to read it from the serial port
// This function is skipped when running in debug mode
// since else it blocks the simulator (waits for a 
// hardware signal from the UART)
////////////////////////////////////////////////////////////
char serial_getch(){

#ifdef DBG_COMPILE
#warning "Debug build, serial_getch will return 0x00"
	return 0x00;
#endif

	// Check for overrun error. Reset the 
	// UART if any.
	if (rcsta.OERR){
		rcsta.CREN = 0;
		rcsta.CREN = 1;
	}
	
	while (!serial_peek());
	return rcreg;
}

/* 
 * 
 * START FREEBMS CHANGES 
 *
 */

BYTE rxbuffer[MAX_MESG_SIZE];	// Buffer for sending messages
BYTE rxptr;						// Pointer for receiving messages
BYTE rxlength;					// Length of message being received
BYTE rxescape;					// Whether previous character was escape character
BYTE mesg_data[MAX_DATA_SIZE];	// Message argument buffer
BYTE chip_id;					// ID of current chip

#if DEBUG==1
unsigned char r0, r1, r2, r3, r4, r5, r6, r7;
unsigned char t0, t1, t2, t3, t4, t5, t6, t7;

// Set t0-t7 to txbuffer for debugging
void txbuffer_to_vars(char *txbuffer) {
	t0 = txbuffer[0];
	t1 = txbuffer[1];
	t2 = txbuffer[2];
	t3 = txbuffer[3];
	t4 = txbuffer[4];
	t5 = txbuffer[5];
	t6 = txbuffer[6];
	t7 = txbuffer[7];
}

// Set r0-r7 to rxbuffer for debugging
void rxbuffer_to_vars() {
	r0 = rxbuffer[0];
	r1 = rxbuffer[1];
	r2 = rxbuffer[2];
	r3 = rxbuffer[3];
	r4 = rxbuffer[4];
	r5 = rxbuffer[5];
	r6 = rxbuffer[6];
	r7 = rxbuffer[7];
}

#endif

// Send a message
BYTE send_message(short mesg_type, short dest_from, BYTE length)
{
	BYTE txbuffer[MAX_MESG_SIZE];
	unsigned int checksum = 0;
	int txlength = 0;
	BYTE i = 0;
	
	// Don't transmit a message that is too long
	if(length > MAX_DATA_SIZE) 
		return false;
		
	// create the message
	txbuffer[0] = 0xFF;				// Message start
	txbuffer[1] = length + 4;		// Length of message + length + id + type + checksum
	txbuffer[2] = dest_from;		// Sender ID or Destination ID
	txbuffer[3] = mesg_type;		// Type of message
	for(i = 0; i < length; ++i)		// Data
		txbuffer[i + 4] = mesg_data[i];

	txlength = length + 4;
						
	for(i = 0; i < txlength; ++i) {
		checksum += (unsigned short) txbuffer[i];

		// Send escape character if necessary
		if((txbuffer[i] == 0xFF && i != 0) || txbuffer[i] == 0xFE)
			serial_printf(0xFE);

		serial_printf(txbuffer[i]);	
	}

	checksum = checksum & 0xFF;
//	checksum = checksum % 100;
	
	// Send escape character if necessary
	if(checksum == 0xFF || checksum == 0xFE)
		serial_printf(0xFE);
	serial_printf(checksum);

#if DEBUG==1
	txbuffer[length + 4] = checksum;
	txbuffer_to_vars(txbuffer);
#ifdef SUPERVISOR
	CANSendMessage( mesg_type, txbuffer, txlength+1,
		CAN_TX_PRIORITY_0 & CAN_TX_STD_FRAME & CAN_TX_NO_RTR_FRAME);
#endif
#endif

	return true;
}

// Receive a message using interrupts
BYTE receive_message() {
	BYTE input;
	int i = 0, checksum = 0;

	if(serial_peek()) { // If there is a character pending delivery
		input = serial_getch();

		// If its '0xFF' and no escape character, start of new message
		if(input == 0xFF && rxescape == false) {
			rxptr = 0;
			rxlength = 0;

		} else {
			// If we got an escape sequence, set flag and continue
			if(input == 0xFE && rxescape == false) {
				rxescape = true;
			} else { // Else store character
				if(rxptr == 0) {
					rxlength = input;
				} 

				rxbuffer[rxptr] = input;
				++rxptr;
			}
		}
		
		// If rxptr is equal to or longer than the expected length, or ptr is invalid
		if((rxptr >= rxlength && rxptr > 0)
		|| rxptr >= MAX_MESG_SIZE - 1) {

			// Checksum for data verification
			checksum = 0xFF;
			for(i = 0; i < rxptr-1; ++i)
				checksum += (unsigned short) rxbuffer[i];
			checksum = checksum & 0xFF;
//			checksum = checksum % 100;

			// If checksums don't match, discard the message
			if(checksum != rxbuffer[rxptr-1]) {
				rxptr = 0;
				rxlength = 0;
				rxescape = false;

				return false;
			} else {
				rxptr = 0;
				rxlength = 0;
				rxescape = false;
#if DEBUG==1
				rxbuffer_to_vars();
#endif				
				return true;
			}
		}
	}
	
	return false;
}